import * as crypto from "node:crypto";

const pingMessageMapping = new Map<string, PingMessage>();

class PingMessage {

    uuid:number[];
    version:number[];

    constructor(uuid:string) {
        this.uuid = this.stringToNumberArray(uuid);
        this.version = this.generateVersion();
    }

    getBuffer() {
        return Buffer.from(this.getMessage());
    }

    stringToNumberArray(val:string) {
        const res = [];
        for(let i = 0; i < val.length; i += 2) {
            const seg = val.substring(i, i + 2);
            const num = parseInt(seg, 16);
            res.push(num)
        }
        return res;
    }

    generateVersion() {
        return [...crypto.randomBytes(24)];
    }

    getTimeBytes() {
        const timestamp = Math.ceil(new Date().getTime() / 1000);
        const timestampBinary = timestamp.toString(2);
        let i = timestampBinary.length;
        let res = [];
        while (i > 0) {
            const seg = timestampBinary.substring(i-8 >= 0 ? i - 8 : 0, i);
            const b = parseInt(seg, 2);
            res.push(b);
            i = i - 8;
        }
        return res;
    }

    getDataBytes() {
        const res = [];
        const status = 0;
        res.push(status);
        res.push(...this.uuid);
        res.push(...this.version);
        res.push(...this.getTimeBytes());
        return res;

    }

    getMessage() {

        const res = [];
        const startFlag = 170;
        const endFlag = 221;

        const mainBlock = [];
        const dataBytes = this.getDataBytes();
        const dataLength = dataBytes.length;

        // 前置4个字节
        // 第一位命令类型
        // 第二位命令码
        // 第三第四 数据长度
        const prev = [0, 2, 0, dataLength ];
        mainBlock.push(...prev);
        mainBlock.push(...dataBytes);

        let crc = 0;
        for (const b of mainBlock) {
            crc += b;
        }

        res.push(startFlag);
        res.push(...mainBlock);
        res.push(crc);
        res.push(endFlag);
        return res;
    }

}

export default {
    createPingMessage(uuid:string) {
        let pingMessage = pingMessageMapping.get(uuid);
        if (!pingMessage) {
            pingMessage = new PingMessage(uuid);
            pingMessageMapping.set(uuid, pingMessage);
        }
        return pingMessage.getBuffer();
    }
};